#!/u01/dbaperl/bin/perl -w
# (C) 2007-2012 Alibaba Group Holding Limited

# Authors:
# thomas.wangyj@alibaba-inc.com

#  Copyright (C) 2012 TaoBao Co.,Ltd.
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#  Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use strict;
use warnings FATAL => 'all';

use Carp qw(croak);
use MHA::BinlogManager;
use MHA::BinlogPosFindManager;
use MHA::BinlogPosFinder;
use MHA::BinlogPosFinderXid;
use MHA::BinlogPosFinderElp;
use MHA::NodeConst;
use MHA::NodeUtil;
use MHA::SlaveUtil;
use Getopt::Long;
use Pod::Usage;
use File::Basename;
use File::Copy;
use Sys::Hostname;
use DBI;

use constant Relay_Log_SQL    => "select \@\@global.relay_log as Value";

our $ROTATE_EVENT             = 4;
our $FORMAT_DESCRIPTION_EVENT = 15;


GetOptions(
  \my %opt, qw/
    help
    version
    l_slaveip=s
    l_slaveport=i
    l_slavepos=i
    h_slaveid=i
    h_slavedir=s
    h_slaveprefix=s
    is_apply=s
    /,
) or pod2usage(2);

####################### option help

if ( $opt{help} ) {
  pod2usage(2);
}

if ( $opt{version} ) {
  print "diff_apply_logs version 1.0 \n";
  exit 0;
}

####################### option handle

unless ( $opt{l_slaveip} ) {
  croak "--l_slaveip (low slave ip) must be set.\n";
}

unless ( $opt{l_slaveport} ) {
  croak "--l_slaveport (low slave port) must be set.\n";
}

unless ( $opt{l_slavepos} ) {
  croak "--l_slavepos (low slave position) must be set.\n";
}

unless ( $opt{h_slaveid} ) {
  croak "--h_slaveid (high slave server id) must be set.\n"
}

unless ( $opt{h_slavedir} ) {
  croak "--h_slavedir (high slave relay dir) must be set.\n";
}

unless ( $opt{h_slaveprefix} ) {
  croak "--h_slaveprefix (high slave relay prefix) must be set.\n";
}

unless ( $opt{is_apply} ) {
  croak "--is_apply (the differ binlog if applied in low slave) must be set.\n";
}

&main();

####################### get relay file

sub get_relay_file ($$) {
  my $relay_prefix = shift;
  my $relay_dir    = shift;

  opendir my $dir, "$relay_dir";
  my @files = grep { m/^$relay_prefix\.[0-9]+/ } readdir $dir;
  @files    = sort @files;
  closedir $dir;

  return @files;
}

sub get_version () {
  my $bv = `mysqlbinlog --version`;
  chomp($bv);
  if ( $bv =~ /Ver (\d+\.\d+)/ ) {
    $bv = $1;
  }
  else {
    croak "mysqlbinlog version not found.\n";
  }

  my $v = `mysql --version`;
  chomp($v);
  if ( $v =~ /Distrib (\d+\.\d+\.\d+)/ ) {
    $v  = $1;
  }
  else {
    croak "mysql version not found.\n";
  }
  
  return $bv,$v;
}


####################### find post relay pos
sub find_post_relay_pos () {
  my $relay_dir   = shift;
  my $relay_file  = shift;
  my $m_server_id = shift;
  my $target_rmlp = shift;
  my $found;

  my $p = new MHA::BinlogHeaderParser(
    dir            => $relay_dir,
    file           => $relay_file,
    self_server_id => $m_server_id,
    target_rmlp    => $target_rmlp,
    debug          => 0,
  );

  $p->open_binlog();

  while ( my ( $event_type, $server_id, $event_length, $end_log_pos ) =
    $p->parse_forward_header() )
  {
    next if ( $server_id == $p->{self_server_id} );
    next if ( $event_type == $FORMAT_DESCRIPTION_EVENT );
    if ( $event_type == $ROTATE_EVENT ) {
      $end_log_pos = $p->parse_master_rotate_event( $p->{prev_pos} );
    }   

    if ( $end_log_pos >= $p->{target_rmlp} ) {
      $found = 1;
      $p->close_binlog();
      return ( $found, $p->{current_pos} );
    }
  }  

  $found = 0;
  $p->close_binlog();
  return $found;
}




####################### main for API

sub main () {
######get relay pos
  my $p             = new MHA::BinlogPosFinderElp();
  $p->{server_id}   = $opt{h_slaveid};
  $p->{relay_dir}   = $opt{h_slavedir};
  $p->{target_rmlp} = $opt{l_slavepos};
  my $found         = 0;
  my $start_rlp     = 0;
  my ( $mysql_bv,$mysql_v ) = get_version();
  my %result;
  
  my @relay_file    = get_relay_file($opt{h_slaveprefix},$opt{h_slavedir});
  %result           = $p->find_starting_relay_pos($relay_file[$#relay_file]);
  #print $result{start_rlp}," ",$result{status}," ",$result{start_rlf},"\n";

######get differ binlog file 
  my $max_num               = $relay_file[$#relay_file];
  $max_num                  =~ m/(.*)\.([0-9]+)/;
  $max_num                  = sprintf( "%06d", $2 );
  my $m                     = new MHA::BinlogManager();
  $m->{dir}                 = $opt{h_slavedir};
  $m->{prefix}              = $opt{h_slaveprefix};
  $m->{end_num}             = $max_num;
  $m->{handle_raw_binlog}   = 1;
  $m->{mysql_version}       = $mysql_v;
  $m->{mysqlbinlog_version} = $mysql_bv;
  my $differ_binlog_file    = "/tmp/slave_for_".$p->{target_rmlp};

  if ( $result{status} == 0 ) {
    my $file_status = $m->concat_all_binlogs_from($result{start_rlf},$result{start_rlp},$differ_binlog_file);
  } 
  else {
    ( $found, $start_rlp ) = &find_post_relay_pos($opt{h_slavedir},$relay_file[$#relay_file],$opt{h_slaveid},$opt{l_slavepos});
    if ($found) {
      my $file_status = $m->concat_all_binlogs_from($relay_file[$#relay_file],$start_rlp,$differ_binlog_file);
      print "I found!\n";
    }  
    else {
      croak "Can not find relay pos from master pos, please check if option l_slavepos correct.\n";
    }
  }

######apply differ binlog file
  if ( $opt{is_apply} eq 'Y' ) {
    my $apply = system("mysqlbinlog $differ_binlog_file | mysql -uroot -p123 -h$opt{l_slaveip} -P$opt{l_slaveport}");

    if ( $apply == 0 ) {
      print "Apply $differ_binlog_file succesfully to Slave $opt{l_slaveip}.\n";
    } else {
      print "Apply $differ_binlog_file error to Slave $opt{l_slaveip}.\n";
    }
  } 
  elsif ( $opt{is_apply} eq 'N' ) {
    print "The differ binlog is $differ_binlog_file ,please apply manual.\n";
  }
  else {
    croak "The option is_apply input error.\n";
  }
     
}


# ############################################################################
# Documentation
# ############################################################################

=pod

=head1 NAME

diff_apply_logs is a different binlog find and apply tool for Mysql.

=head1 SYNOPSIS

diff_apply_logs --l_slaveip=s --l_slaveport=i --l_slavepos=i --h_slaveid=i --h_slavedir=s
